﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;

namespace Xml2Yaml
{
    public sealed class XPathNavigatorUtil
    {
        private XPathNavigatorUtil()
        {
        }

        #region xml Path Navigator

        public static XPathNavigator LoadXPath(Stream stream)
        {
            return new XPathDocument(stream).CreateNavigator();
        }

        public static XPathNavigator LoadXPath(XmlReader reader)
        {
            return new XPathDocument(reader).CreateNavigator();
        }

        public static XPathNavigator LoadXPath(string xml)
        {
            using (StringReader sr = new StringReader(xml))
            {
                using (XmlReader reader = XmlTextReader.Create(sr))
                {
                    return LoadXPath(reader);
                }
            }
        }

        public static XPathNavigator LoadFromFile(string fileName)
        {
            using (XmlReader reader = XmlTextReader.Create(fileName))
            {
                return LoadXPath(reader);
            }
        }

        public static XPathNavigator TryParseXmlDocument(StringBuilder sb)
        {
            try
            {
                if (sb[0] == 65279) //remove utf-8 header
                {
                    sb.Remove(0, 1);
                }
                return XPathNavigatorUtil.LoadXPath(sb.ToString());
            }
            catch (XmlException
#if (DEBUG)
 ex
#endif
)
            {
#if (DEBUG)
                Trace.TraceWarning(ex.Message);
#endif
            }
            return null;
        }

        public static XPathNodeIterator Select(XPathNavigator navigator, string xmlPath)
        {
            return Select(navigator, xmlPath, null);
        }

        public static XPathNodeIterator Select(XPathNavigator navigator, string xmlPath, IXmlNamespaceResolver namespaceResolover)
        {
            try
            {
                return navigator == null ? null : navigator.Select(
                    xmlPath,
                    namespaceResolover == null ? GetNamespaceResolver(navigator) : namespaceResolover
                    );
            }
            catch (System.Xml.XPath.XPathException)
            {
                return null;
            }
        }
        public static XPathNavigator SelectSingleNode(XPathNavigator navigator, string xmlPath)
        {
            return SelectSingleNode(navigator, xmlPath, null);
        }

        public static XPathNavigator SelectSingleNode(XPathNavigator navigator, string xmlPath, IXmlNamespaceResolver namespaceResolover)
        {
            try
            {
                return navigator == null ? null : navigator.SelectSingleNode(
                    xmlPath,
                    namespaceResolover == null ? GetNamespaceResolver(navigator) : namespaceResolover
                    );
            }
            catch (System.Xml.XPath.XPathException)
            {
                return null;
            }
        }

        public static int ReadNodeInt32Value(XPathNavigator navigator)
        {
            return ReadNodeInt32Value(navigator, ".");
        }

        public static int ReadNodeInt32Value(XPathNavigator navigator, string xmlPath)
        {
            XPathNavigator node = SelectSingleNode(navigator, xmlPath);
            return node == null ? 0 : int.Parse(node.Value, CultureInfo.InvariantCulture);
        }

        public static string ReadNodeStringValue(XPathNavigator navigator)
        {
            return ReadNodeStringValue(navigator, ".");
        }

        public static string ReadNodeStringValue(XPathNavigator navigator, string xmlPath)
        {
            XPathNavigator node = SelectSingleNode(navigator, xmlPath);
            return node == null ? string.Empty : node.Value;
        }

        public static bool ReadNodeBooleanValue(XPathNavigator navigator, string xmlPath)
        {
            XPathNavigator node = SelectSingleNode(navigator, xmlPath);
            return YamlUtil.IsTrue(node == null ? string.Empty : node.Value);
        }

        public static string GetAttribute(XPathNavigator navigator, string attribute)
        {
            return navigator == null ? string.Empty : navigator.GetAttribute(attribute, string.Empty);
        }

        public static IXmlNamespaceResolver GetNamespaceResolver(XPathNavigator navigator)
        {
            return navigator == null ? null : AddNamespaces(new XmlNamespaceManager(navigator.NameTable));
        }

        public static IXmlNamespaceResolver GetNamespaceResolver(XmlNameTable nameTable)
        {
            return nameTable == null ? null : AddNamespaces(new XmlNamespaceManager(nameTable));
        }

        public static XmlNamespaceManager AddNamespaces(XmlNamespaceManager namespaceManager)
        {
            if (namespaceManager != null)
            {
                IDictionary<string, string> map = namespaceManager.GetNamespacesInScope(XmlNamespaceScope.All);
                //namespaceManager.NameTable
                foreach (KeyValuePair<string, string> pair in map)
                {
                    namespaceManager.AddNamespace(pair.Key, pair.Value);
                }
            }
            return namespaceManager;
        }
        #endregion

        #region XmlSerizlizer

        public static T Parse<T>(string xml)
        {
            if (!string.IsNullOrEmpty(xml))
            {
                try
                {
                    using (XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(xml)))
                    {
                        XmlSerializer serializer = new XmlSerializer(typeof(T));
                        if (serializer.CanDeserialize(reader))
                        {
                            return (T)serializer.Deserialize(reader);
                        }
                    }
                }
#if (DEBUG)
                catch (XmlException ex)
                {
                    Trace.TraceWarning(ex.Message);
                }
#else
                catch (XmlException)
                {
                }
#endif
            }
            return default(T);
        }


        /// <summary>
        /// Serialize an object into an XML string
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string SerializeToXml<T>(T valueObject)
        {
            // To cut down on the size of the xml being sent to the database, we'll strip
            // out this extraneous xml.
            StringWriter output = new StringWriter(new StringBuilder(), CultureInfo.InvariantCulture);
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            serializer.Serialize(output, valueObject);
            return output.ToString();

        }

        #endregion
    }

}